package com.chang.springsecurity.config;

import com.chang.springsecurity.handler.RestAccessDeniedHandler;
import com.chang.springsecurity.handler.RestAuthenticationEntryPoint;
import com.chang.springsecurity.security.filter.JwtAuthenticationFilter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;

import java.util.Arrays;

@Configuration       // 配置类注解
@EnableWebSecurity   // 开启WebSecurity配置
public class SecurityConfig {

    @Autowired
    private JwtAuthenticationFilter jwtAuthenticationFilter;

    @Autowired
    private RestAuthenticationEntryPoint restAuthenticationEntryPoint;

    @Autowired
    private RestAccessDeniedHandler restAccessDeniedHandler;

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        // 认证内容
        http
            .cors(cors -> cors.configurationSource(corsConfigurationSource()))            // 跨域配置
            .csrf(csrf -> csrf.disable())                                                 // 前后端分离项目通常关闭CSRF
            .sessionManagement(sm -> sm.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
            .authorizeHttpRequests(authz -> authz
                    .requestMatchers("/auth/**","/public/**").permitAll()             // 公共路径，无需登录都能访问，permitAll
                    .requestMatchers("/user/**").hasAnyRole("USER","ADMIN")      // 普通路径,需要登录才能访问，即管理员和用户都能访问，hasAnyRole（可访问的角色列表）
                    .requestMatchers("/admin/**").hasRole("ADMIN")                      // 管理员路径，只有管理员才能访问，hasRole（可访问的角色）
                    .anyRequest().authenticated()                                         // 其余普通路径，需登录才能访问
            )
            .addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class)    // 配置自定义过滤器
            .exceptionHandling(eh -> eh
                    .authenticationEntryPoint(restAuthenticationEntryPoint)                // 401 未认证认证失败处理器
                    .accessDeniedHandler(restAccessDeniedHandler)                          // 403 权限不足
            )
            .logout(logout -> logout.disable());                                           // 禁用默认登出（STATELESS下登出无意义，前端删Token即可）

        return http.build();
    }

    /**
     * CORS 跨域配置：允许前端跨域请求
     */
    @Bean
    public CorsConfigurationSource corsConfigurationSource() {
        CorsConfiguration corsConfiguration = new CorsConfiguration();
        corsConfiguration.setAllowedOriginPatterns(Arrays.asList("*"));
        corsConfiguration.setAllowedMethods(Arrays.asList("GET","POST","PUT","DELETE","OPTIONS"));
        corsConfiguration.setAllowedHeaders(Arrays.asList("*"));
        corsConfiguration.setAllowCredentials(true);
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", corsConfiguration);
        return source;
    }
    @Bean
    public AuthenticationManager authenticationManager(AuthenticationConfiguration authConfig) throws Exception {
        return authConfig.getAuthenticationManager();
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}
